package demo26;

import java.util.LinkedList;

/**
 * 写一个固定容量的同步容器，拥有put和get方法，以及getCount方法，能够支持2个生产者线程以及10个消费者线程的阻塞调用。
 * wait和notifyAll方法来实现。
 */
public class MyContainer1 {
    final private LinkedList<Object> list = new LinkedList<Object>();
    final private int MAX = 10; //最多十个元素
    private int count = 0;

    public synchronized void put(Object t) {
        while (list.size() == MAX) { //想想为什么用while而不是if
            try {
                this.wait();//wait会释放锁
                //1. 假如有一个线程t1满足了size==MAX的情况，然后一进来就被wait了，并且释放了锁,线程t1等待在这里。
                //2. 这个时候其他的线程t2获取了锁，也调用了put的方法，这个时候判断是满的，所以t2线程也是释放了锁并wait等待在这里了。
                //3. 这个时候其他线程t3获取了锁，这时候调用的是get方法了。get方法执行完之后会size-1,这个时候size不是满的了，然后notifyAll，叫醒等待的线程。
                //4. 这时候刚好叫醒了等待的线程t1,t1线程重新获取锁，并从wait()方法继续往下执行，因为我们已经判断过if了，所以往下执行add的操作了，这个时候size+1了，又变成满的了，最后又调用了notifyAll方法。叫醒等待的线程
                //5. 这个时候刚刚好叫醒的是t2线程了，t2线程重新获取锁了，并从wait方法继续往下执行，因为if条件判断过了，所以这个时候不会再判断size是不是满了，但是这个时候size已经是满的了，但还是继续往下执行add的操作，所以size最终就变成了MAX+1。
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        list.add(t);
        ++count;
        this.notifyAll(); //通知消费者进程进行消费
        //notify只叫醒一个，叫醒的可能还是生产者，所有线程一直wait，程序就卡死了，所以用notifyAll
    }

    public synchronized Object get() {
        Object t = null;
        while (list.size() == 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        t = list.removeFirst();
        count--;
        this.notifyAll();  //通知生产者进程进行生产
        return t;
    }

    public static void main(String[] args) {
        MyContainer1 pc = new MyContainer1();
        for (int i = 0; i < 10; i++) {  //10个消费者
            new Thread(() -> {
                for (int j = 0; j < 5; j++) {  //每个消费者最多消费5个
                    System.out.println("消费者线程" + Thread.currentThread().getName() + ",开始消费： " + pc.get());
                }
            }, "c" + i).start();
        }
        //启动生产者线程
        for (int i = 0; i < 2; i++) { //2个生产者
            new Thread(() -> {
                for (int j = 0; j < 25; j++) {  //每个生产者最多生产25个
                    pc.put(Thread.currentThread().getName() + " " + j);
                }
            }, "p" + i).start();
        }
    }

}